//
//  Mg_image_lib.m
//  V3d_Cocoa
//
//  Created by yang liang on 11-3-23.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import	<stdio.h>
#import	<stdlib.h>
#import	<ctype.h>
#import	<string.h>
#import "Mg_image_lib.h"
#import	"Mg_utilities.h"

static int Warnings = 1;

static int error(char *msg, const char *arg)
{	fprintf(stderr,"\nError in TIFF library:\n   ");
	fprintf(stderr,msg,arg);
	fprintf(stderr,"\n");
	return 1;
}

Stack_Plane *Select_Plane(Stack *a_stack, int plane)  // Build an image for a plane of a stack
{	static Stack_Plane My_Image;
	
	if (plane < 0 || plane >= a_stack->depth)
		return (NULL);
	My_Image.kind   = a_stack->kind;
	My_Image.width  = a_stack->width;
	My_Image.height = a_stack->height;
	My_Image.array  = a_stack->array + plane*a_stack->width*a_stack->height*a_stack->kind;
	return (&My_Image);
}

static uint32 *get_raster(int npixels, char *routine)
{	static uint32 *Raster = NULL;
	static int     Raster_Size = 0;                           //  Manage read work buffer
	
	if (npixels < 0)
    {	free(Raster);
		Raster_Size = 0;
		Raster      = NULL;
    }
	else if (npixels > Raster_Size)
    {	Raster_Size = npixels;
		Raster = (uint32 *) Guarded_Realloc(Raster,sizeof(uint32)*Raster_Size,routine);
    }
	return (Raster);
}

static void read_directory(TIFF *tif, Image *image, char *routine)   //  Used by all readers
{	uint32 *raster;
	uint8  *row;
	int     width, height;
	
	width  = image->width;
	height = image->height;
	raster = get_raster(width*height,routine);
	
	row = image->array;
	
	if (image->kind != GREY16)
		
    {	int i, j;
		uint32 *in;
		uint8  *out;
		
		if (TIFFReadRGBAImage(tif,width,height,raster,0) == 0)
			error("read of tif failed in read_directory()", NULL);
		
		in = raster;
		if (image->kind == GREY)
        {	for (j = height-1; j >= 0; j--)
			{	out = row;
				for (i = 0; i < width; i++)
				{	uint32 pixel = *in++;
					*out++ = TIFFGetR(pixel);
				}
				row += width;
			}
        }
		else
        {	for (j = height-1; j >= 0; j--)
			{	out = row;
				for (i = 0; i < width; i++)
				{	uint32 pixel = *in++;
					*out++ = TIFFGetR(pixel);
					*out++ = TIFFGetG(pixel);
					*out++ = TIFFGetB(pixel);
				}
				row += width*3;
			}
        }
    }
	
	else
		
    {	int tile_width, tile_height;
		
		if (TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tile_width))    // File is tiled  
			{	int x, y;
				int i, j;
				int m, n;
				uint16 *buffer = (uint16 *) raster;
				uint16 *out, *in;
			
				TIFFGetField(tif, TIFFTAG_TILELENGTH, &tile_height);
			
				for (y = 0; y < height; y += tile_height)
				{	if (y + tile_height > height)
					n = height - y;
				else
					n = tile_height;
				for (x = 0; x < width; x += tile_width)
					{	TIFFReadTile(tif, buffer, x, y, 0, 0);
						if (x + tile_width > width)
							m = width - x;
						else
							m = tile_width;
						for (j = 0; j < n; j++)
						{	out = (uint16 *) (row + 2*(j*width + x));
							in  = buffer + j*tile_width;
							for (i = 0; i < m; i++)
								*out++ = *in++; 
						}
					}
					row += n*width*2;
				}
			}
		
		else    // File is striped
			
        {	int     y;
			
			for (y = 0; y < height; y++)
				{	TIFFReadScanline(tif, row, y, 0);
					row += width*2;
				}
			}
		}
}

TIFF *Open_Tiff(const char *file_name, char *mode)
{	TIFF *tif;
	
	if (Warnings)
    {	Warnings = 0;
		TIFFSetWarningHandler(NULL);
    }
	
	tif = TIFFOpen(file_name,mode);
	if (tif == NULL)
		error("cannot opening TIFF file %s", file_name);
	return (tif);
} 

static int determine_kind(TIFF *tif)   //  Determine nature of current tif image
{	short bits, channels, photo;
	
	TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bits);
	TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &channels);
	TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photo);
	if (photo <= 1)
    {	if (channels > 1)
			error("Black and white tiff has more than 1 channel!",NULL);
		if (bits == 16)
			return (GREY16);
		else
			return (GREY);
    }
	else
		return (COLOR);
}

typedef struct __Stack
{	struct __Stack *next;
    int             vsize;
    Stack           stack;
} _Stack;

static _Stack *Free_Stack_List = NULL;
static int    Stack_Offset, Stack_Inuse;

static inline Stack *new_stack(int vsize, char *routine)
{	_Stack *object;
	
	if (Free_Stack_List == NULL)
    {	object = (_Stack *) Guarded_Malloc(sizeof(_Stack),routine);
		Stack_Offset = ((char *) &(object->stack)) - ((char *) object);
		object->vsize = vsize;
		object->stack.array = (uint8 *)Guarded_Malloc(vsize,routine);
		Stack_Inuse += 1;
    }
	else
    {	object = Free_Stack_List;
		Free_Stack_List = object->next;
		if (object->vsize < vsize)
        {	object->vsize = vsize;
			object->stack.array = (uint8 *)Guarded_Realloc(object->stack.array,
														   vsize,routine);
        }
    }
	return (&(object->stack));
}

void Kill_Stack(Stack *stack)
{	free(stack->array);
	free(((char *) stack) - Stack_Offset);
	Stack_Inuse -= 1;
}

Stack *Read_Stack(const char *file_name)
{	Stack *stack;
	
	TIFF  *tif;
	int    depth, width, height, kind;
	
	tif = Open_Tiff(file_name,"r");
	depth = 1;
	while (TIFFReadDirectory(tif))
		depth += 1;
	TIFFClose(tif);
	
	tif = Open_Tiff(file_name,"r");
	TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width);
	TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height);
	
	kind  = determine_kind(tif);
	stack = new_stack(depth*height*width*kind,"Read_Stack");
	
	stack->width  = width;
	stack->height = height;
	stack->depth  = depth;
	stack->kind   = kind;
	
	{	int d;
		
		d = 0;
		while (1)
		{	read_directory(tif,Select_Plane(stack,d),"Read_Stack");
			
			d += 1;
			if (!TIFFReadDirectory(tif)) break;
			
			TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width);
			TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height);
			if (width != stack->width || height != stack->height)
				error("Images of stack are not of the same dimensions!",NULL);
			
			kind = determine_kind(tif);
			if (kind != stack->kind)
				error("Images of stack are not of the same type (GREY, GREY16, or COLOR)!",NULL);
		}
	}
	
	TIFFClose(tif);
	
	return (stack);
}



